package com.example.a46;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanExpressionContext;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;

@Configuration
@SuppressWarnings("all")
public class A46Application {

    public static void main(String[] args) throws Exception {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(A46Application.class);
        DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();

        ContextAnnotationAutowireCandidateResolver resolver = new ContextAnnotationAutowireCandidateResolver();
        resolver.setBeanFactory(beanFactory);

        Field home = Bean1.class.getDeclaredField("home");
//        test1(context, resolver, home);

        Field aeg = Bean1.class.getDeclaredField("age");
//        test2(context, resolver, aeg);

        Field bean3 = Bean2.class.getDeclaredField("bean3");
//        test3(context, resolver, bean3);

        Field value = Bean4.class.getDeclaredField("value");
        test3(context, resolver, value);

        /*
            小结：
                @Value 装配底层 按类型装配的步骤
                    1. 查看需要的类型是否为 Optional，是 则进行封装（非延迟），否则向下走
                    2. 查看需要的类型是否为 ObjectFactory 或 ObjectProvider，是 则进行封装（延迟），否则向下走
                    3. 查看需要的类型（成员或参数）上是否用 @Lazy 修饰，是，则返回代理，否则向下走
                    4. 解析 @Value 的值
                        1. 如果需要的值是字符串，先解析 ${ }，再解析 #{ }
                        2. 不是字符串，需要用 TypeConverter 转换
                    5. 看需要的类型是否为 Stream、Array、Collection、Map，是，则按集合处理，否则向下走
                    6. 在 BeanFactory 的 resolvableDependencies 中找有没有类型合适的对象注入，没有向下走
                    7. 在 BeanFactory 及父工厂中找类型匹配的 bean 进行筛选，筛选时会考虑 @Qualifier 及泛型
                    8. 结果个数为 0 抛出 NoSuchBeanDefinitionException 异常
                    9. 如果结果 > 1，再根据 @Primary 进行筛选
                    10. 如果结果仍 > 1，再根据成员名或变量名进行筛选
                    11. 结果仍 > 1，抛出 NoUniqueBeanDefinitionException 异常
         */

    }

    private static void test3(AnnotationConfigApplicationContext context, ContextAnnotationAutowireCandidateResolver resolver, Field field) {
        DependencyDescriptor dd1 = new DependencyDescriptor(field, false);
        // 获取 @Value 的内容
        String value = resolver.getSuggestedValue(dd1).toString();
        System.out.println(value);

        // 解析 ${}
        value = context.getEnvironment().resolvePlaceholders(value);
        System.out.println(value);
        System.out.println(value.getClass());

        Object bean3 = context.getBeanFactory().getBeanExpressionResolver().evaluate(value, new BeanExpressionContext(context.getBeanFactory(), null));

        // 必要的时候做类型转换
        Object result = context.getBeanFactory().getTypeConverter().convertIfNecessary(bean3, dd1.getDependencyType());
        System.out.println(result);
    }

    private static void test2(AnnotationConfigApplicationContext context, ContextAnnotationAutowireCandidateResolver resolver, Field field) {
        DependencyDescriptor dd1 = new DependencyDescriptor(field, false);
        // 获取 @Value 的内容
        String value = resolver.getSuggestedValue(dd1).toString();
        System.out.println(value);

        // 解析 ${}
        value = context.getEnvironment().resolvePlaceholders(value);
        System.out.println(value);
        System.out.println(value.getClass());

        // 必要的时候做类型转换
        Object age = context.getBeanFactory().getTypeConverter().convertIfNecessary(value, dd1.getDependencyType());
        System.out.println(age.getClass());
    }

    private static void test1(AnnotationConfigApplicationContext context, ContextAnnotationAutowireCandidateResolver resolver, Field field) {
        DependencyDescriptor dd1 = new DependencyDescriptor(field, false);
        String value = resolver.getSuggestedValue(dd1).toString();
        System.out.println(value);

        // 解析 ${}
        value = context.getEnvironment().resolvePlaceholders(value);
        System.out.println(value);
    }

    public class Bean1 {

        @Value("${JAVA_HOME}")
        private String home;

        @Value("18")
        private int age;
    }

    public class Bean2 {

        @Value("#{@bean3}") // SpringEL  表达式    #{SpEL}
        private Bean3 bean3;
    }

    @Component("bean3")
    public class Bean3 {
    }

    static class Bean4 {
        @Value("#{'hello, ' +'${JAVA_HOME}'}")
        private String value;
    }
}
